package com.example.component;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.example.entity.chat.Message;
import com.example.repository.MessageRepository;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

@ServerEndpoint(value = "/imserver/{username}") //端口路径
@Component //容器管理
public class WebSocketServer {

    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class); //日志记录器

    private static MessageRepository messageRepository; //消息储存库

    // 记录离线消息
    private static final ConcurrentHashMap<String, List<Message>> offlineMessages = new ConcurrentHashMap<>();

    @Autowired
    public void setMessageRepository(MessageRepository messageRepository) {
        WebSocketServer.messageRepository = messageRepository;
    }

    // 记录当前在线用户的会话
    private static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    // 连接建立成功调用的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {
        sessionMap.put(username, session); //将用户放入容器
        log.info("有新用户加入，username={}，当前在线人数为：{}", username, sessionMap.size());

//        // 发送当前在线用户列表给所有客户端
//        broadcastUserList();

        // 发送历史聊天记录给新用户
        sendChatHistory(session, username);

        // 发送离线消息给新用户
        List<Message> messages=offlineMessages.get(username);
        if(messages!=null){
            for (Message message:messages){
                JSONObject jsonMessage=createMessageJson(
                        message.getSender(),
                        message.getReceiver(),
                        message.getContent(),
                        message.getTimestamp(),
                        message.getMessageType()
                );
                sendMessage(jsonMessage.toString(), session);
            }
            offlineMessages.remove(username);
        }
    }

    // 连接关闭调用的方法
    @OnClose
    public void onClose(Session session, @PathParam("username") String username) {
        sessionMap.remove(username);//移除容器中离开的对象
        log.info("有一连接关闭，移除username={}的用户session，当前在线人数为：{}", username, sessionMap.size());

//        // 更新在线用户列表并广播给所有客户端
//        broadcastUserList();
    }

    // 收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("username") String username) {
        log.info("服务端收到用户username={}的消息:{}", username, message);
        JSONObject obj = new JSONObject(message);
        String toUsername = obj.getStr("to"); // 接收者
        String text = obj.getStr("text"); // 消息内容
        Session toSession = sessionMap.get(toUsername); // 获取接收者的会话

        // 保存消息到数据库
        Message chatMessage = new Message();
        chatMessage.setSender(username);
        chatMessage.setReceiver(toUsername);
        chatMessage.setContent(text);
        chatMessage.setTimestamp(new Date());
        chatMessage.setMessageType("text");
        messageRepository.save(chatMessage); //保存

        JSONObject jsonMessage = createMessageJson(username, toUsername, text, new Date(), "text"); //消息包装成JSON对象

        //如果接收者存在
        if (toSession != null) {
            sendMessage(jsonMessage.toString(), toSession);//发送
            log.info("发送给用户username={}，消息：{}", toUsername, jsonMessage.toString());
        } else {
            //接收者不在线，储存离线消息
            log.info("用户username={}不在线，储存离线消息",toUsername);
            List<Message> messages=offlineMessages.computeIfAbsent(toUsername,k->new CopyOnWriteArrayList<>());
            messages.add(chatMessage);
        }
    }

    // 发生错误时调用的方法
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误", error);
    }

    // 发送聊天历史记录给新用户
    private void sendChatHistory(Session session, String username) {
        List<Message> chatHistory = messageRepository.findBySenderOrReceiver(username, username);
        for (Message chatMessage : chatHistory) {
            try {
                JSONObject jsonMessage = createMessageJson(
                        chatMessage.getSender(),
                        chatMessage.getReceiver(),
                        chatMessage.getContent(),
                        chatMessage.getTimestamp(),
                        chatMessage.getMessageType()
                );
                sendMessage(jsonMessage.toString(), session);
            } catch (Exception e) {
                log.error("发送历史消息失败", e);
            }
        }
    }

    // 创建消息的JSON对象,方便创建jsonObject对象
    private JSONObject createMessageJson(String from, String to, String text, Date timestamp, String messageType) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("from", from);
        jsonObject.set("to", to);
        jsonObject.set("text", text);
        jsonObject.set("timestamp", timestamp);
        jsonObject.set("messageType", messageType);
        return jsonObject;
    }

    // 广播当前在线用户列表
    private void broadcastUserList() {
        JSONObject result = new JSONObject();
        JSONArray array = new JSONArray();
        result.set("users", array);

        //获取所有在线的用户并且存进Array
        for (String username : sessionMap.keySet()) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("username", username);
            array.add(jsonObject);
        }

        //发送给所有客户端
        sendAllMessage(result.toString());
    }

    // 服务器发送消息给客户端
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }

    // 服务器发送消息给所有客户端
    private void sendAllMessage(String message) {
        for (Session session : sessionMap.values()) {
            try {
                log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                log.error("服务端发送消息给客户端失败", e);
            }
        }
    }
}
